package com.tpshion.cloud.core.autoconfig.support;

import com.tpshion.cloud.common.support.Assert;
import com.tpshion.cloud.common.support.CoreException;
import com.tpshion.cloud.common.support.ErrorCode;
import com.tpshion.cloud.core.autoconfig.properties.MinioClientProperties;
import io.minio.MinioClient;
import io.minio.errors.*;
import io.minio.messages.Item;
import io.minio.policy.PolicyType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.xmlpull.v1.XmlPullParserException;

import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

public class MinioTemplate {

    private static final Logger log = LoggerFactory.getLogger(MinioTemplate.class);

    private MinioClient minioClient;

    private MinioClientProperties properties;

    public MinioTemplate(MinioClient client, MinioClientProperties properties){
        this.minioClient = client;
        this.properties = properties;
    }

    /**
     * 简单文件上传
     * @param fileName 文件名称
     * @param inputStream 文件输入流
     * @param contentType 文件类型
     * @param bucketName 存储位置
     * @return
     */
    public UploadResponse simpleUpload(@NotNull String fileName,@NotNull InputStream inputStream,@NotNull String contentType, String bucketName){
        log.info("*** simpleUpload start. fileName:{} contentType:{}",fileName,contentType);
        Assert.notEmpty(fileName, ErrorCode.FILE_NAME_IS_NULL);
        Assert.notNull(inputStream, ErrorCode.FILE_STREAM_IS_NULL);
        Assert.notEmpty(contentType, ErrorCode.FILE_TYPE_IS_NULL);
        try {
            // 检查文件大小，文件超过100MB则报错
            log.info(" file size:{}",getFileSize(inputStream.available()));
            Assert.isTrue(getSizeMB(inputStream.available()) <= 100, ErrorCode.FILE_TOO_BIG);
            if(StringUtils.isEmpty(bucketName)){
                bucketName = properties.getBucketName();
            }

            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(sdf.format(new Date())).append("-")
                       .append(UUID.randomUUID().toString().substring(0,4))
                       .append(fileName.substring(fileName.lastIndexOf(".")));
            String newFileName = stringBuffer.toString();

            //存入bucket不存在则创建，并设置为只读
            if (!minioClient.bucketExists(bucketName)) {
                minioClient.makeBucket(bucketName);
                minioClient.setBucketPolicy(bucketName, "*.*", PolicyType.READ_WRITE);
            }
            // 存储文件
            minioClient.putObject(bucketName, newFileName, inputStream, contentType);
            String filePath = properties.getEndpoint() + "/" + bucketName + "/" + newFileName;
            if(!StringUtils.isEmpty(properties.getPrefix())){
                filePath = properties.getPrefix() + "/" + bucketName + "/" + newFileName;
            }
            log.info("*** filePath:{} file upload is successfully!",filePath);
            return new UploadResponse(filePath,newFileName);
        } catch (Exception e) {
            if(e instanceof CoreException){
                CoreException ex = (CoreException) e;
                throw ex;
            }else{
                log.info("fileName:{} file upload error: {}",fileName, e.getMessage());
                throw new CoreException(ErrorCode.FILE_UPLOAD_ERROR);
            }
        }
    }

    public UploadResponse simpleUpload(@NotNull String fileName,@NotNull InputStream inputStream,@NotNull String contentType){
        return simpleUpload(fileName,inputStream,contentType,null);
    }

    /**
     * 删除文件
     * @param fileName 文件名称
     */
    public void delete(@NotNull String fileName, String bucketName){
        Assert.notEmpty(fileName,ErrorCode.FILE_NAME_IS_NULL);
        if(StringUtils.isEmpty(bucketName)){
            bucketName = properties.getBucketName();
        }
        // 检查文件是否存在
        Assert.isTrue(checkFileExist(fileName,bucketName),ErrorCode.FILE_NOT_EXIST);
        try {
            minioClient.removeObject(bucketName, fileName);
            log.info("*** fileName:{} file delete is successfully!",fileName);
        } catch (Exception e) {
            log.error("fileName:{} file delete error:{}",fileName,e.getMessage());
            throw new CoreException(ErrorCode.FILE_DELETE_ERROR);
        }
    }

    public void delete(@NotNull String fileName){
        delete(fileName, null);
    }

    /**
     * 检查文件是否存在
     * @param fileName
     * @param bucketName
     */
    public boolean checkFileExist(@NotNull String fileName, String bucketName){
        Assert.notEmpty(fileName,ErrorCode.FILE_NAME_IS_NULL);
        try {
            if(StringUtils.isEmpty(bucketName)){
                bucketName = properties.getBucketName();
            }
            minioClient.statObject(bucketName, fileName);
            return true;
        } catch (Exception e) {
            log.info("fileName:{} file is not exist!",fileName);
            return false;
        }
    }

    public boolean checkFileExist(@NotNull String fileName){
        return checkFileExist(fileName, null);
    }

    /**
     * 获取文件大小
     * @param size 字节大小
     * @return
     */
    public String getFileSize(long size) {
        // 如果字节数少于1024，则直接以B为单位，否则先除于1024，后3位因太少无意义
        double value = (double) size;
        if (value < 1024) {
            return String.valueOf(value) + " B";
        } else {
            value = new BigDecimal(value / 1024).setScale(2, BigDecimal.ROUND_DOWN).doubleValue();
        }
        // 如果原字节数除于1024之后，少于1024，则可以直接以KB作为单位
        // 因为还没有到达要使用另一个单位的时候
        // 接下去以此类推
        if (value < 1024) {
            return String.valueOf(value) + " KB";
        } else {
            value = new BigDecimal(value / 1024).setScale(2, BigDecimal.ROUND_DOWN).doubleValue();
        }
        if (value < 1024) {
            return String.valueOf(value) + " MB";
        } else {
            // 否则如果要以GB为单位的，先除于1024再作同样的处理
            value = new BigDecimal(value / 1024).setScale(2, BigDecimal.ROUND_DOWN).doubleValue();
            return String.valueOf(value) + " GB";
        }
    }

    public double getSizeMB(int size){
        return ((long) size / (1024.0 * 1024.0));
    }

    /**
     * 获取文件列表
     */
    public List<FileObject> getFileObjects(){
        List<FileObject> list = new ArrayList<>();
        try {
            boolean found = minioClient.bucketExists(properties.getBucketName());
            Assert.isTrue(found, ErrorCode.FAIL.getCode(), "bucket不存在");
            Iterable<io.minio.Result<Item>> myObjects = minioClient.listObjects(properties.getBucketName());
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            for (io.minio.Result<Item> result : myObjects) {
                Item item = result.get();
                FileObject fileObject = new FileObject();
                fileObject.setObjectName(item.objectName());
                fileObject.setSize(getFileSize(item.objectSize()));
                fileObject.setLastModified(sdf.format(item.lastModified()));
                fileObject.setPath(properties.getPrefix() + "/" + properties.getBucketName() + "/" + item.objectName());
                list.add(fileObject);
            }
            return list;
        } catch (Exception e) {
            if(e instanceof CoreException){
                CoreException ex = (CoreException) e;
                throw ex;
            }else{
                log.error("getFileObjects error: {}", e.getMessage(),e);
                throw new CoreException(ErrorCode.FILE_LIST_ERROR);
            }
        }
    }

    public FileInfo getFileInfo(){
        FileInfo fileInfo = new FileInfo();
        fileInfo.setBucketName(properties.getBucketName());
        long realSize = 42949672960L;
        List<FileObject> list = new ArrayList<>();
        try {
            boolean found = minioClient.bucketExists(properties.getBucketName());
            Assert.isTrue(found, ErrorCode.FAIL.getCode(), "bucket不存在");
            Iterable<io.minio.Result<Item>> myObjects = minioClient.listObjects(properties.getBucketName());
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            long size = 0;
            for (io.minio.Result<Item> result : myObjects) {
                Item item = result.get();
                FileObject fileObject = new FileObject();
                fileObject.setObjectName(item.objectName());
                fileObject.setSize(getFileSize(item.objectSize()));
                fileObject.setLastModified(sdf.format(item.lastModified()));
                fileObject.setPath(properties.getPrefix() + "/" + properties.getBucketName() + "/" + item.objectName());
                list.add(fileObject);
                size = size + item.objectSize();
            }
            fileInfo.setTotalSize(getFileSize(size));
            fileInfo.setPercent(getPercent(size,realSize));
            fileInfo.setFileObjects(list);
            return fileInfo;
        } catch (Exception e) {
            if(e instanceof CoreException){
                CoreException ex = (CoreException) e;
                throw ex;
            }else{
                log.error("getFileObjects error: {}", e.getMessage(),e);
                throw new CoreException(ErrorCode.FILE_LIST_ERROR);
            }
        }
    }

    private long getPercent(long count, long total) {
        if(total==0){
            return 0;
        }
        BigDecimal currentCount = new BigDecimal(count);
        BigDecimal totalCount = new BigDecimal(total);
        BigDecimal divide = currentCount.divide(totalCount,2,BigDecimal.ROUND_HALF_UP);
        long value = divide.multiply(new BigDecimal(100)).longValue();
        if(value == 0){
            return 2;
        }
        return value;
    }
}
